package com.recharge.aop;

import com.recharge.common.annotation.OperationLogDetail;
import com.recharge.common.result.ResultDO;
import com.recharge.common.util.Constant;
import com.recharge.common.util.RedisUtil;
import com.recharge.domain.vo.UserDetailDO;
import com.recharge.domain.vo.UserLoginDO;
import com.recharge.domain.vo.UserOperationLogDo;
import com.recharge.service.UserOperationLogService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <h1>描述: 操作日志</h1>
 *
 * @author kui
 * @date 2020/8/22 15:44
 */
@Aspect
@Component
public class OperationLog {
    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    private UserOperationLogService service;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 此处的切点可以是注解的方式，也可以用包名的方式达到 相同的效果
     * '@Pointcut("execution(* com.wwj.springboot.service.impl.*.*(..))")'
     * '@Pointcut("@annotation(com.wwj.springboot.annotation.OperationLogDetail)")'
     */
//    @Pointcut("execution(* com.recharge.service.impl.*.*(..))")
//    public void operationLog() {
//    }
//
//    /**
//     * 环绕增强，相当于MethodInterceptor
//     */
//    @Around("operationLog()")
//    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//        Object res = null;
//        try {
//            res = joinPoint.proceed();
//            return res;
//        } finally {
//            try {
//                //方法执行完成后增加日志
//                addOperationLog(joinPoint, res);
////                System.out.println("环绕增强执行完成...");
//            } catch (Exception e) {
//                System.err.println("操作日志写入失败：" + e.getMessage());
//            }
//        }
//    }

//    /**
//     * 添加操作日志
//     *
//     * @param joinPoint
//     * @param res
//     */
//    private void addOperationLog(ProceedingJoinPoint joinPoint, Object res) {
//        // 获取类名
//        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
//        // 获取方法名
//        String methodName = joinPoint.getSignature().getName();
//        // 获取操作类型枚举
//        OperationType operationType = OperationType.get(className, methodName);
//
//        if (operationType == null){
//            return;
//        }
//
//        if (res == null) {
//            log.error("操作日志添加失败, 缺失参数!");
//            return;
//        }
//
//        //创建操作日志对象
//        UserOperationLogDo operationLogDo = new UserOperationLogDo();
//
//        //获取当前用户信息
//        UserLoginDO userLoginDO = (UserLoginDO) SecurityUtils.getSubject().getPrincipal();
//
//        //获取操作描述模板
//        String description = operationType.getDescription();
//
//        // 设置操作人
//        if (userLoginDO != null){
//            operationLogDo.setUserId(userLoginDO.getId());
//            operationLogDo.setUserName(userLoginDO.getUsername());
//            operationLogDo.setMerchantId(userLoginDO.getMerchantId());
//
//            //占位符替换
//            if (userLoginDO.getId() != null){
//                description = description.replace("{id}", userLoginDO.getId());
//            }
//            if (userLoginDO.getUsername() != null){
//                description = description.replace("{username}", userLoginDO.getUsername());
//            }
//            if (userLoginDO.getMerchantId() != null){
//                description = description.replace("{merchantId}", userLoginDO.getMerchantId());
//            }
//        }
//        // 设置操作描述、模块名、操作类型
//        operationLogDo.setDescription(description);
//        operationLogDo.setModuleName(operationType.getModuleName());
//        operationLogDo.setOpType(operationType.getOperationType());
//
//        // 日志写入数据库
//        service.add(operationLogDo);
//        System.out.println(operationLogDo.toString());
//
//    }
    @Pointcut("@annotation(com.recharge.common.annotation.OperationLogDetail)")
    public void operationLogAnnotation() {
    }

    /**
     * 环绕增强，相当于MethodInterceptor
     */
    @Around("operationLogAnnotation()")
    public Object doAroundAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        try {
            res = joinPoint.proceed();
            return res;
        } finally {
            try {
                //方法执行完成后增加日志
                addOperationLogAnnotation(joinPoint, res);
//                System.out.println("环绕增强执行完成...");
            } catch (Exception e) {
                logger.debug("操作日志写入失败：" + e.getMessage());
            }
        }
    }

    private void addOperationLogAnnotation(ProceedingJoinPoint joinPoint, Object res) {
        if (res == null) {
            logger.debug("操作日志: 添加失败, 缺失参数!");
            return;
        }

        // 获取类名
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();

        //创建操作日志对象
        UserOperationLogDo operationLogDo = new UserOperationLogDo();

        //获取当前用户信息
        UserLoginDO userLoginDO = null;
        try {
            userLoginDO = (UserLoginDO) SecurityUtils.getSubject().getPrincipal();
            userLoginDO = (UserLoginDO) redisUtil.get(Constant.USER + userLoginDO.getUsername());
        } catch (Exception e) {
            logger.debug("操作日志: 没有登录用户信息");
            return;
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        OperationLogDetail annotation = signature.getMethod().getAnnotation(OperationLogDetail.class);

        if (annotation == null) {
            logger.debug("操作日志: 注解异常");
            return;
        }

        //获取操作描述模板
//        String description = getDetail(((MethodSignature) joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs(), annotation.detail());
        String description = annotation.detail();

        // 设置操作人
        if (userLoginDO != null) {
            operationLogDo.setUserId(userLoginDO.getId());
            operationLogDo.setUserName(userLoginDO.getUsername());
            operationLogDo.setMerchantId(userLoginDO.getMerchantId());

            //占位符替换
//            description = description.replace("{id}", userLoginDO.getId());
//            description = description.replace("{merchantId}", userLoginDO.getMerchantId());
            description = description.replace("{username}", userLoginDO.getUsername());
            UserDetailDO userDetailDO = userLoginDO.getUserDetailDO();
            if (userDetailDO != null&&!StringUtils.isEmpty(userDetailDO.getRealName())) {
                description = description.replace("{realName}", userDetailDO.getRealName());
            }
            description += "(操作人:" + userLoginDO.getUsername() + ")";
        }
        // 设置操作描述、模块名、操作类型
        operationLogDo.setDescription(description);
        operationLogDo.setModuleName(String.format("%s::%s", className, methodName));
        operationLogDo.setOpType(annotation.operationType().getValue());

        // 日志写入数据库
        ResultDO add = null;
        try {
            add = service.add(operationLogDo);
        } catch (Exception e) {
            logger.debug("操作日志: 写入数据库失败" + e.getMessage());
        }
        if (add == null || !add.isSuccess()) {
            logger.debug("操作日志: 添加操作日志失败");
        }
    }

    /**
     * 对注解中占位符处理
     * @param argNames 方法参数名称数组
     * @param args 方法参数数组
     * @param detail 需要替换信息
     * @return 返回处理后的描述
     */
//    private String getDetail(String[] argNames, Object[] args, String detail){
//        int index = detail.indexOf("{{");
//        if (index == -1){
//            return detail;
//        }
//        String substring = detail.substring(index + 2, detail.indexOf("}}"));
//        try {
//            if (substring.split(".").length > 1){
//                System.out.println("暂时不支持调用对象属性");
//            }else {
//                for(int i = 0;i < argNames.length;i++){
//                    Object k = argNames[i];
//                    Object v = args[i];
//                    if (detail.contains(k.toString())){
//                        detail = detail.replace("{{" + k + "}}", v.toString());
//                        break;
//                    }
//                }
//            }
//        }catch (Exception e){
//            e.printStackTrace();
//        }
//        return detail;
//    }

}
